package com.wei.czz.framework.blog.manager;

import com.wei.czz.common.constant.Constant;
import com.wei.czz.common.constant.EntityConstant;
import com.wei.czz.common.constant.RedisConstant;
import com.wei.czz.common.dto.blog.*;
import com.wei.czz.common.dto.common.NameValue;
import com.wei.czz.common.enums.admin.user.UserMessageEnum;
import com.wei.czz.common.enums.blog.*;
import com.wei.czz.common.enums.*;
import com.wei.czz.common.enums.blog.audit.AuditLevelEnum;
import com.wei.czz.common.enums.blog.audit.AuditStatusEnum;
import com.wei.czz.common.enums.blog.audit.AuditTypeEnum;
import com.wei.czz.common.enums.rabbitMQ.BlogMessageEnum;
import com.wei.czz.common.enums.rabbitMQ.MqEnum;
import com.wei.czz.common.exception.CzzException;
import com.wei.czz.common.po.UserPo;
import com.wei.czz.common.po.blog.BlogManageListPo;
import com.wei.czz.common.po.blog.UserIssueBlogListPo;
import com.wei.czz.common.tool.PageDto;
import com.wei.czz.common.utils.CacheKeyUtils;
import com.wei.czz.common.utils.CopyUtils;
import com.wei.czz.common.utils.RandomNumUtils;
import com.wei.czz.common.utils.SecurityUtils;
import com.wei.czz.common.vo.blog.*;
import com.wei.czz.framework.admin.helper.UserHelper;
import com.wei.czz.framework.blog.entity.*;
import com.wei.czz.framework.blog.helper.BlogHelper;
import com.wei.czz.framework.blog.helper.SpaceHelper;
import com.wei.czz.framework.blog.helper.TagHelper;
import com.wei.czz.framework.blog.service.*;
import com.wei.czz.framework.common.helper.CacheHelper;
import com.wei.czz.framework.common.handler.DebounceHandler;
import com.wei.czz.framework.common.handler.ThrottleHandler;
import com.wei.czz.framework.common.handler.redis.RedisHashHandler;
import com.wei.czz.framework.common.handler.redis.RedisStringHandler;
import com.wei.czz.framework.common.handler.redis.RedisHandler;
import com.wei.czz.framework.admin.service.UserMessageService;
import com.wei.czz.framework.common.helper.RabbitHelper;
import com.wei.czz.framework.common.service.AsyncService;
import com.wei.czz.framework.common.service.SnowflakeService;
import com.wei.czz.framework.common.service.TransactionService;
import com.wei.czz.framework.rabbitMQ.message.blog.BlogAuditMessage;
import com.wei.czz.framework.rabbitMQ.message.blog.BlogMessage;
import lombok.AllArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * Created by IntelliJ IDEA.
 *
 * @author wyw
 * date: 2023-12-07 18:23:34
 * className: BlogManager
 * version: 2.0
 * description:
 */
@Component
@AllArgsConstructor
public class BlogManager {

    private static final Logger log = LoggerFactory.getLogger(BlogManager.class);

    private final BlogService blogService;

    private final BlogContentService blogContentService;

    private final BlogIssueService blogIssueService;

    private final BlogStatusService blogStatusService;

    private final BlogAuditService blogAuditService;

    private final BlogFlowService blogFlowService;

    private final BlogTagService blogTagService;

    private final BlogAuditUserStatService blogAuditUserStatService;

    private final SpaceService spaceService;

    private final UserMessageService userMessageService;

    private final SnowflakeService snowflakeService;

    private final AsyncService asyncService;

    private final RedissonClient redissonClient;

    private final UserHelper userHelper;

    private final BlogHelper blogHelper;

    private final TagHelper tagHelper;

    private final SpaceHelper spaceHelper;

    private final RedisStringHandler redisStringHandler;

    private final RedisHandler redisHandler;

    private final RedisHashHandler redisHashHandler;

    private final RabbitHelper rabbitHelper;

    private final CacheHelper cacheHelper;

    private final ThrottleHandler throttleHandler;

    private final DebounceHandler debounceHandler;

    private final TransactionService transactionService;


    public void deleteSpace(List<Long> spaceIdList) {
        /*
            获取空间关联博客
         */
        List<BlogEntity> blogList = blogService.getSpaceBlogList(spaceIdList);
        if (!blogList.isEmpty()) {
            throw new CzzException(ResultEnum.REQUEST_FAIL.getCode(), "删除空间列表中，存在空间关联博客关系，无法删除!");
        }

        /*
            删除博客空间
         */
        spaceService.deleteSpace(spaceIdList);
    }

    public String saveBlog(BlogVo blogVo) {
        // 获取操作用户
        UserPo userPo = SecurityUtils.getUser();
        Date date = new Date();

        /*
            获取博客
         */
        BlogEntity blog = Optional.ofNullable(blogVo.getId()).map(blogHelper::getBlog).orElse(null);

        BlogContentEntity blogContent = null;
        List<BlogTagEntity> blogTagList = Collections.emptyList();
        if (Objects.nonNull(blog)) {
            /*
                判断博客状态是否可保存
             */
            List<Integer> canEditList = Arrays.asList(BlogStatusEnum.DRAFT.getValue(), BlogStatusEnum.AUDIT_REJECT.getValue());
            if (!canEditList.contains(blog.getStatus())) {
                log.info("当前博客状态不可保存。status={}", BlogStatusEnum.getName(blog.getStatus()));
                throw new CzzException(ResultEnum.ILLEGAL_FAIL);
            }

            if (!Objects.equals(userPo.getUserId(), blog.getUserId())) {
                log.error("操作用户不是博客作者");
                throw new CzzException(ResultEnum.ILLEGAL_FAIL);
            }

            /*
                获取博客内容
             */
            blogContent = blogContentService.find(blog.getId(), blog.getVersion());

            /*
                获取博客标签
             */
            blogTagList = blogTagService.findList(blog.getId(), blog.getVersion());
        }
        // list 转 map
        Map<String, BlogTagEntity> blogTagMap = blogTagList.stream().collect(
                Collectors.toMap(BlogTagEntity::getTagName, Function.identity(), (o1, o2) -> o1)
        );

        // 获取博客主键
        Long id = Optional.ofNullable(blogVo.getId()).orElse(snowflakeService.get());
        // 获取博客编号
        String code = Optional.ofNullable(blog)
                .map(BlogEntity::getCode)
                .orElse(blogHelper.generateCode(userPo.getUserId(), id));
        // 版本号
        Integer version = Optional.ofNullable(blog).map(BlogEntity::getVersion).orElse(CommonEnum.ONE.getValue());

        if (Objects.isNull(blog)) {
            /*
                保存博客
             */
            BlogEntity saveBlog = new BlogEntity();
            saveBlog.setId(id)
                    .setUserId(userPo.getUserId())
                    .setSpaceId(blogVo.getSpaceId())
                    .setCode(code)
                    .setTitle(blogVo.getTitle())
                    .setGuide(blogVo.getGuide())
                    .setFirstPicture(blogVo.getFirstPicture())
                    .setLabel(blogVo.getLabel())
                    .setViewNum(CommonEnum.ZERO.getValue())
                    .setLikeNum(CommonEnum.ZERO.getValue())
                    .setCollectNum(CommonEnum.ZERO.getValue())
                    .setCommentNum(CommonEnum.ZERO.getValue())
                    .setShowStatement(blogVo.getShowStatement())
                    .setOpenComment(blogVo.getOpenComment())
                    .setStatus(BlogStatusEnum.DRAFT.getValue())
                    .setVersion(version);
            blogService.insert(saveBlog);
        } else {
            // 删除缓存
            String redisKey = CacheKeyUtils.getRedisHashKey(EntityConstant.BLOG);
            String hashKey = blog.getId().toString();
            Long count = redisHashHandler.delete(redisKey, hashKey);
            log.info("删除缓存成功。count={}", count);

            /*
                修改博客
             */
            BlogEntity editBlog = new BlogEntity();
            editBlog.setId(blog.getId())
                    .setSpaceId(blogVo.getSpaceId())
                    .setTitle(blogVo.getTitle())
                    .setGuide(blogVo.getGuide())
                    .setFirstPicture(blogVo.getFirstPicture())
                    .setLabel(blogVo.getLabel())
                    .setShowStatement(blogVo.getShowStatement())
                    .setOpenComment(blogVo.getOpenComment());
            blogService.edit(editBlog);

        }

        if (Objects.isNull(blogContent)) {
            /*
                新增博客正文
             */
            BlogContentEntity saveBlogContent = new BlogContentEntity();
            saveBlogContent.setBlogId(id)
                    .setCode(code)
                    .setVersion(version)
                    .setContent(blogVo.getContent());
            blogContentService.saveOrEdit(saveBlogContent);

        } else {
            /*
                修改博客正文
             */
            BlogContentEntity updateBlogContent = new BlogContentEntity();
            updateBlogContent.setId(blogContent.getId())
                   .setContent(blogVo.getContent());
            blogContentService.saveOrEdit(updateBlogContent);

        }

        List<BlogTagEntity> saveBlogTagList = new ArrayList<>();
        List<BlogTagEntity> updateBlogTagList = new ArrayList<>();

        List<String> tagList = blogVo.getTagList();
        int sort = 1;
        for (String tagName : tagList) {
            BlogTagEntity blogTag = blogTagMap.remove(tagName);
            if (Objects.isNull(blogTag)) {
                BlogTagEntity saveBlogTag = new BlogTagEntity();
                saveBlogTag.setBlogId(id)
                        .setTagName(tagName)
                        .setSort(sort++)
                        .setVersion(version)
                        .setDeleteStatus(CommonEnum.ZERO.getValue())
                        .setUpdateTime(date)
                        .setUpdateUser(StringUtils.EMPTY)
                        .setUpdateUserId(CommonEnum.ZERO.getLongValue())
                        .setCreateTime(date)
                        .setCreateUser(userPo.getUsername())
                        .setCreateUserId(userPo.getUserId());
                saveBlogTagList.add(saveBlogTag);
            } else {
                BlogTagEntity updateBlogTag = new BlogTagEntity();
                updateBlogTag.setId(blogTag.getId())
                        .setSort(sort++)
                        .setUpdateTime(date)
                        .setUpdateUser(userPo.getUsername())
                        .setUpdateUserId(userPo.getUserId());
                updateBlogTagList.add(updateBlogTag);
            }
        }
        for (BlogTagEntity blogTag : blogTagMap.values()) {
            BlogTagEntity updateBlogTag = new BlogTagEntity();
            updateBlogTag.setId(blogTag.getId())
                    .setDeleteStatus(CommonEnum.ONE.getValue())
                    .setUpdateTime(date)
                    .setUpdateUser(userPo.getUsername())
                    .setUpdateUserId(userPo.getUserId());
            updateBlogTagList.add(updateBlogTag);
        }

        /*
            保存博客标签
         */
        if (!saveBlogTagList.isEmpty()) {
            blogTagService.batchSave(saveBlogTagList);
        }
        /*
            修改博客标签
         */
        if (!updateBlogTagList.isEmpty()) {
            blogTagService.batchUpdate(updateBlogTagList);
        }

        if (Objects.nonNull(blog)) {
            // 延时双删
            String redisKey = CacheKeyUtils.getRedisHashKey(EntityConstant.BLOG);
            String hashKey = blog.getId().toString();
            Runnable runnable = () -> {
                // 删除缓存
                Long count = redisHashHandler.delete(redisKey, hashKey);
                log.info("延时双删缓存成功。count={}", count);
            };
            debounceHandler.debounceRun(runnable, redisKey + Constant.SPLIT + hashKey, 1500, TimeUnit.MILLISECONDS);
        }
        // 删除缓存
        this.deleteBlogCache(userPo.getUserId());

        return id.toString();
    }

    @Transactional
    public void commitBlog(CommitBlogVo commitBlogVo) {
        // 获取操作用户
        UserPo userPo = SecurityUtils.getUser();

        /*
            获取博客
         */
        BlogEntity blog = blogHelper.getBlog(commitBlogVo.getId());

        List<Integer> canEditList = Arrays.asList(BlogStatusEnum.DRAFT.getValue(), BlogStatusEnum.AUDIT_REJECT.getValue());
        if (!canEditList.contains(blog.getStatus())) {
            log.info("当前博客状态不可提交审核。status={}", BlogStatusEnum.getName(blog.getStatus()));
            throw new CzzException(ResultEnum.ILLEGAL_FAIL);
        }

        if (!Objects.equals(userPo.getUserId(), blog.getUserId())) {
            log.error("【博客提交审核】操作用户不是博客作者");
            throw new CzzException(ResultEnum.ILLEGAL_FAIL);
        }

        // 删除缓存
        String redisKey = CacheKeyUtils.getRedisHashKey(EntityConstant.BLOG);
        String hashKey = blog.getId().toString();
        Long count = redisHashHandler.delete(redisKey, hashKey);
        log.info("【博客提交审核】删除缓存成功。count={}", count);


        /*
            修改博客
         */
        BlogEntity updateBlog = new BlogEntity();
        updateBlog.setId(blog.getId())
                .setStatus(BlogStatusEnum.AUDIT.getValue());
        blogService.edit(updateBlog);

        /*
            保存博客状态记录
         */
        BlogStatusEntity saveBlogStatus = new BlogStatusEntity();
        saveBlogStatus.setBlogId(blog.getId())
                .setAuditId(CommonEnum.ZERO.getLongValue())
                .setPreStatus(blog.getStatus())
                .setCurStatus(updateBlog.getStatus())
                .setRemark(StringUtils.EMPTY);
        blogStatusService.saveOrEdit(saveBlogStatus);

        /*
            保存博客审核记录
         */
        BlogAuditEntity saveBlogAudit = new BlogAuditEntity();
        saveBlogAudit.setBlogId(blog.getId())
                .setType(AuditTypeEnum.NEW_BLOG.getValue())
                .setLevel(AuditLevelEnum.ONE.getValue())
                .setAuditUserId(CommonEnum.ZERO.getLongValue())
                .setAuditStatus(AuditStatusEnum.WAIT.getValue())
                .setAuditRejectType(CommonEnum.MINUS_ONE.getValue())
                .setAuditRemark(StringUtils.EMPTY)
                .setRemark(StringUtils.EMPTY);
        blogAuditService.saveOrEdit(saveBlogAudit);

        // 发送审核MQ消息
        BlogAuditMessage blogAuditMessage = new BlogAuditMessage();
        blogAuditMessage.setAuditId(saveBlogAudit.getId())
                .setBlogId(blog.getId())
                .setAuditType(AuditTypeEnum.NEW_BLOG.getValue())
                .setAuditLevel(AuditLevelEnum.ONE.getValue());
        rabbitHelper.send(MqEnum.BLOG_AUDIT, blogAuditMessage);

        // 删除缓存
        this.deleteBlogCache(userPo.getUserId());

        // 延时双删
        Runnable runnable = () -> {
            // 删除缓存
            Long _count = redisHashHandler.delete(redisKey, hashKey);
            log.info("【博客提交审核】延时双删缓存成功。count={}", _count);
        };
        debounceHandler.debounceRun(runnable, redisKey + Constant.SPLIT + hashKey, 1500, TimeUnit.MILLISECONDS);

        asyncService.execute(() -> {
            /*
                保存博客流水
             */
            BlogFlowEntity blogFlow = new BlogFlowEntity();
            blogFlow.setBlogId(blog.getId())
                    .setTitle(blog.getTitle())
                    .setGuide(blog.getGuide())
                    .setFirstPicture(blog.getFirstPicture())
                    .setLabel(blog.getLabel())
                    .setShowStatement(blog.getShowStatement())
                    .setOpenComment(blog.getOpenComment())
                    .setVersion(blog.getVersion());
            blogFlowService.saveOrEdit(blogFlow);
        });
    }

    public void cacheBlog(List<Long> idList) {

        /*
            批量获取博客
         */
        List<BlogEntity> blogList = blogService.getBlogList(idList);
        // list 转 map
        Map<String, BlogEntity> blogMap = CopyUtils.listToMap(blogList, blog -> blog.getId().toString(), Function.identity());

        /*
            缓存博客
         */
        String redisKey = CacheKeyUtils.getRedisHashKey(EntityConstant.BLOG);
        redisHashHandler.putAll(redisKey, blogMap);

    }

    public void cacheAllBlog() {

        /*
            缓存全部博客
         */
        List<BlogEntity> blogList = blogService.getBlogList();
        // list 转 map
        Map<String, BlogEntity> blogMap = CopyUtils.listToMap(blogList, blog -> blog.getId().toString(), Function.identity());

        /*
            缓存博客
         */
        String redisKey = CacheKeyUtils.getRedisHashKey(EntityConstant.BLOG);
        redisHashHandler.putAll(redisKey, blogMap);
    }

    public PageDto<UserBlogListDto> getSelfBlogPageList(UserBlogFormVo userBlogFormVo) {
        // 获取正在查询自己博客列表的用户id
        Long optUserId = SecurityUtils.getUserId();
        // 设置用户id
        userBlogFormVo.setUserId(optUserId);

        /*
            缓存请求参数
         */
        String cacheKey = CacheKeyUtils.getParamCacheKey(EntityConstant.BLOG);
        cacheHelper.cache(cacheKey, userBlogFormVo);

        String userBlogListKey = userBlogFormVo.getUserBlogPageList();
        // 查询Redis，获取用户自己的博客信息列表
        PageDto<UserBlogListDto> pageDto = redisStringHandler.get(userBlogListKey);
        if (pageDto != null) {
            throttleHandler.throttleRun(() -> {
                // 尝试更新缓存键的过期时间
                Long num = redisHandler.tryUpdateKeyExpire(userBlogListKey, 150, RandomNumUtils.random5Minutes(10));
                log.info("刷新缓存成功。redisKey={} num={}", userBlogListKey, num);
            }, userBlogListKey, 1500, TimeUnit.MILLISECONDS);
            return pageDto;
        }

        /*
            分页获取博客
         */
        PageDto<BlogEntity> blogPageDto = blogService.getUserBlogPageList(userBlogFormVo);
        if (blogPageDto.isEmpty()) {
            log.info("分页获取用户博客为空");
            return blogPageDto.rebuild();
        }

        List<BlogEntity> blogList = blogPageDto.getList();
        // 列表映射
        Function<BlogEntity, UserBlogListDto> function = blog -> {
            UserBlogListDto userBlogListDto = CopyUtils.map(blog, UserBlogListDto.class);
            userBlogListDto.setLabelName(BlogLabelEnum.getName(blog.getLabel()))
                    .setStatusName(BlogStatusEnum.getName(blog.getStatus()));
            return userBlogListDto;
        };
        List<UserBlogListDto> list = CopyUtils.mapList(blogList, function);

        // 封装
        pageDto = blogPageDto.rebuild(list);

        // 缓存用户自己的博客列表五分钟
        redisStringHandler.set(userBlogListKey, pageDto, RandomNumUtils.random5Minutes(10), TimeUnit.SECONDS);

        return pageDto;
    }

    public UserBlogPageEnumDto getSelfBlogPageEnum() {

        /*
            获取页面参数
         */
        String cacheKey = CacheKeyUtils.getParamCacheKey(EntityConstant.BLOG);
        UserBlogFormVo userBlogFormVo = cacheHelper.get(cacheKey, UserBlogFormVo.class);

        List<NameValue<Integer>> hotStatusList = new ArrayList<>(2);
        hotStatusList.add(new NameValue<>("热门", CommonEnum.ZERO.getValue()));
        hotStatusList.add(new NameValue<>("正常", CommonEnum.ONE.getValue()));

        List<NameValue<Integer>> eliteStatusList = new ArrayList<>(2);
        eliteStatusList.add(new NameValue<>("推荐", CommonEnum.ZERO.getValue()));
        eliteStatusList.add(new NameValue<>("正常", CommonEnum.ONE.getValue()));

        List<NameValue<Integer>> fastStatusList = new ArrayList<>();
        fastStatusList.add(new NameValue<>(BlogStatusEnum.ISSUE.getName(), BlogStatusEnum.ISSUE.getValue()));
        fastStatusList.add(new NameValue<>(BlogStatusEnum.AUDIT_PASS.getName(), BlogStatusEnum.AUDIT_PASS.getValue()));
        fastStatusList.add(new NameValue<>(BlogStatusEnum.DRAFT.getName(), BlogStatusEnum.DRAFT.getValue()));
        fastStatusList.add(new NameValue<>(BlogStatusEnum.OFF_LINE.getName(), BlogStatusEnum.OFF_LINE.getValue()));
        fastStatusList.add(new NameValue<>(BlogStatusEnum.HIDE.getName(), BlogStatusEnum.HIDE.getValue()));

        UserBlogPageEnumDto userBlogPageEnumDto = new UserBlogPageEnumDto();
        userBlogPageEnumDto.setUserBlogFormVo(userBlogFormVo)
                .setSpaceList(spaceHelper.getSpaceList())
                .setLabelList(BlogLabelEnum.getList())
                .setStatusList(BlogStatusEnum.getList())
                .setHotStatusList(hotStatusList)
                .setEliteStatusList(eliteStatusList)
                .setFastStatusList(fastStatusList)
                .setIssueStatus(BlogStatusEnum.ISSUE.getValue());
        return userBlogPageEnumDto;
    }

    public PageDto<UserIssueBlogListDto> getSelfIssueBlogPageList(UserBlogFormVo userBlogFormVo) {
        // 获取正在查询自己博客列表的用户id
        Long optUserId = SecurityUtils.getUserId();
        // 设置用户id
        userBlogFormVo.setUserId(optUserId);

        /*
            缓存请求参数
         */
        String cacheKey = CacheKeyUtils.getParamCacheKey(EntityConstant.BLOG);
        cacheHelper.cache(cacheKey, userBlogFormVo);

        String redisKey = userBlogFormVo.getUserIssueBlogPageList();
        // 查询Redis，获取用户自己的已发布博客分页数据
        PageDto<UserIssueBlogListDto> pageDto = redisStringHandler.get(redisKey);
        if (pageDto != null) {
            throttleHandler.throttleRun(() -> {
                // 尝试更新缓存键的过期时间
                Long num = redisHandler.tryUpdateKeyExpire(redisKey, 150, RandomNumUtils.random5Minutes(10));
                log.info("【获取用户已发布博客】刷新缓存成功。redisKey={} num={}", redisKey, num);
            }, redisKey, 1500, TimeUnit.MILLISECONDS);
            return pageDto;
        }

        /*
            分页获取博客
         */
        PageDto<UserIssueBlogListPo> blogPageDto = blogService.getUserIssueBlogPageList(userBlogFormVo);
        if (blogPageDto.isEmpty()) {
            log.info("分页获取用户已发布博客为空");
            return blogPageDto.rebuild();
        }

        List<UserIssueBlogListPo> blogList = blogPageDto.getList();
        // 列表映射
        Function<UserIssueBlogListPo, UserIssueBlogListDto> function = blog -> {
            UserIssueBlogListDto userIssueBlogListDto = CopyUtils.map(blog, UserIssueBlogListDto.class);
            userIssueBlogListDto.setLabelName(BlogLabelEnum.getName(blog.getLabel()))
                    .setStatusName(BlogStatusEnum.getName(blog.getStatus()))
                    .setHotStatusName(CommonEnum.ZERO.getValue().equals(blog.getHotStatus()) ? "热门" : "正常")
                    .setEliteStatusName(CommonEnum.ZERO.getValue().equals(blog.getEliteStatus()) ? "推荐" : "正常");
            return userIssueBlogListDto;
        };
        List<UserIssueBlogListDto> list = CopyUtils.mapList(blogList, function);

        // 封装
        pageDto = blogPageDto.rebuild(list);

        // 缓存用户自己的博客列表五分钟
        redisStringHandler.set(redisKey, pageDto, RandomNumUtils.random5Minutes(10), TimeUnit.SECONDS);

        return pageDto;
    }

    public SaveBlogPageEnumDto getSaveBlogPageEnum() {

        /*
            获取系统标签名称列表
         */
        List<String> tagNameList = tagHelper.getTagList();

        /*
            获取用户博客空间列表
         */
        List<NameValue<String>> spaceList = spaceHelper.getSpaceList();

        SaveBlogPageEnumDto saveBlogPageEnumDto = new SaveBlogPageEnumDto();
        saveBlogPageEnumDto.setTagNameList(tagNameList)
               .setSpaceList(spaceList)
               .setLabelList(BlogLabelEnum.getList());
        return saveBlogPageEnumDto;
    }

    public PageDto<BlogManageListDto> getManageBlogPageList(BlogManageFormVo blogManageFormVo) {

        /*
            缓存请求参数
         */
        String cacheKey = CacheKeyUtils.getParamCacheKey(EntityConstant.BLOG_MANAGE);
        cacheHelper.cache(cacheKey, blogManageFormVo);

        /*
            从缓存获取博客分页数据
         */
        String redisKey = blogManageFormVo.getManageBlogListKey();
        PageDto<BlogManageListDto> pageDto = redisStringHandler.get(redisKey);
        if (Objects.nonNull(pageDto)) {
            log.info("从缓存中获取到博客分页数据");
            throttleHandler.throttleRun(() -> {
                // 尝试更新缓存键的过期时间
                Long num = redisHandler.tryUpdateKeyExpire(redisKey, 150, RandomNumUtils.random5Minutes(10));
                log.info("【获取博客列表】刷新缓存成功。redisKey={} num={}", redisKey, num);
            }, redisKey, 1500, TimeUnit.MILLISECONDS);

            return pageDto;
        }

        String lockKey = RedisConstant.LOCK + redisKey + RedisConstant.SPLIT + RandomNumUtils.getRandomNum(3);

        // 获取Redis分布式锁对象
        RLock lock = redissonClient.getLock(lockKey);
        // 尝试加锁
        boolean bool = lock.tryLock();
        log.info("分页获取博客，尝试加锁完成。lockKey={} bool={}", lockKey, bool);
        if (!bool) {
            // 加锁
            lock.lock();
            log.info("分页获取博客，加锁成功");
        }


        try {
            if (!bool) {
                // double check
                pageDto = redisStringHandler.get(redisKey);
                if (Objects.nonNull(pageDto)) {
                    log.info("double check 从缓存中获取到博客分页数据");
                    return pageDto;
                }
            }

            /*
                分页获取博客
             */
            PageDto<BlogManageListPo> page = blogService.getManageBlogPageList(blogManageFormVo);
            if (page.isEmpty()) {
                log.info("分页获取用户已发布博客为空");
                return page.rebuild();
            }

            List<BlogManageListPo> blogList = page.getList();
            // 列表映射
            Function<BlogManageListPo, BlogManageListDto> function = blog -> {
                BlogManageListDto blogManageListDto = CopyUtils.map(blog, BlogManageListDto.class);
                blogManageListDto.setLabelName(BlogLabelEnum.getName(blog.getLabel()))
                        .setStatusName(BlogStatusEnum.getName(blog.getStatus()))
                        .setShowStatementName(CommonEnum.ZERO.getValue().equals(blog.getShowStatement()) ? "开启" : "关闭")
                        .setOpenCommentName(CommonEnum.ZERO.getValue().equals(blog.getOpenComment()) ? "开启" : "关闭")
                        .setHotStatusName(CommonEnum.ZERO.getValue().equals(blog.getHotStatus()) ? "热门" : "正常")
                        .setEliteStatusName(CommonEnum.ZERO.getValue().equals(blog.getEliteStatus()) ? "推荐" : "正常");
                return blogManageListDto;
            };
            List<BlogManageListDto> list = CopyUtils.mapList(blogList, function);

            // 封装
            pageDto = page.rebuild(list);


            // 缓存管理员可以管理的博客列表五分钟
            redisStringHandler.set(redisKey, pageDto, RandomNumUtils.random5Minutes(10), TimeUnit.SECONDS);

            return pageDto;
        } finally {
            // 判断当前锁对象是否还持有锁 并 持有该锁的线程与当前业务线程一致
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                // 解锁
                lock.unlock();
            }
        }
    }

    public BlogManagePageEnumDto getManageBlogPageEnum() {
        /*
            获取页面参数
         */
        String cacheKey = CacheKeyUtils.getParamCacheKey(EntityConstant.BLOG_MANAGE);
        BlogManageFormVo blogManageFormVo = cacheHelper.get(cacheKey, BlogManageFormVo.class);

        List<NameValue<Integer>> hotStatusList = new ArrayList<>(2);
        hotStatusList.add(new NameValue<>("热门", CommonEnum.ZERO.getValue()));
        hotStatusList.add(new NameValue<>("正常", CommonEnum.ONE.getValue()));

        List<NameValue<Integer>> eliteStatusList = new ArrayList<>(2);
        eliteStatusList.add(new NameValue<>("推荐", CommonEnum.ZERO.getValue()));
        eliteStatusList.add(new NameValue<>("正常", CommonEnum.ONE.getValue()));

        BlogManagePageEnumDto blogManagePageEnumDto = new BlogManagePageEnumDto();
        blogManagePageEnumDto.setBlogManageFormVo(blogManageFormVo)
                .setLabelList(BlogLabelEnum.getList())
                .setStatusList(BlogStatusEnum.getList())
                .setHotStatusList(hotStatusList)
                .setEliteStatusList(eliteStatusList);
        return blogManagePageEnumDto;
    }

    public BlogUpdateDto getBlogUpdate(Long id) {
        // 获取请求用户主键
        Long optUserId = SecurityUtils.getUserId();

        /*
            获取博客
         */
        BlogEntity blog = blogHelper.getBlog(id);
        if (!BlogStatusEnum.getCanEditList().contains(blog.getStatus())) {
            log.info("【获取博客修改数据】博客状态不可编辑。status={}", BlogStatusEnum.getName(blog.getStatus()));
            throw new CzzException(ResultEnum.ILLEGAL_FAIL);
        }

        if (!Objects.equals(optUserId, blog.getUserId())) {
            log.error("【获取博客修改数据】博客作者不是操作用户");
            throw new CzzException(ResultEnum.ILLEGAL_FAIL);
        }

        /*
            获取博客正文
         */
        BlogContentEntity blogContent = blogContentService.find(blog.getId(), blog.getVersion());

        /*
            获取博客标签
         */
        List<BlogTagEntity> blogTagList = blogTagService.findList(blog.getId(), blog.getVersion());
        // 收集
        List<String> tagList = blogTagList.stream()
                .sorted(Comparator.comparing(BlogTagEntity::getSort))
                .map(BlogTagEntity::getTagName)
                .collect(Collectors.toList());

        // 对象映射
        BlogUpdateDto blogUpdateDto = CopyUtils.map(blog, BlogUpdateDto.class);
        blogUpdateDto.setContent(blogContent.getContent());
        blogUpdateDto.setTagList(tagList);

        return blogUpdateDto;
    }

    public Map<String, Object> getBlogOnCache(Long id) {
        String redisKey = CacheKeyUtils.getRedisHashKey(EntityConstant.BLOG);
        String hashKey = id.toString();
        // 查询Redis缓存，获取角色缓存信息
        BlogEntity blog = redisHashHandler.get(redisKey, hashKey);
        if (blog == null) {
            log.info("博客缓存不存在");
            throw new CzzException(ResultEnum.REQUEST_FAIL.getCode(),
                    "您查询的博客缓存信息不存在，请确认，如有问题，请联系管理员处理。");
        }
        Map<String, Object> result = new HashMap<>(8);
        result.put("structKey", redisKey);
        result.put("valueKey", hashKey);
        result.put("data", blog);
        return result;
    }

    @Transactional
    public void withdrawAudit(Long id) {
        // 获取当前正在取消博客审核的用户id
        UserPo userPo = SecurityUtils.getUser();
        Date date = new Date();

        /*
            获取博客
         */
        BlogEntity blog = blogHelper.getBlog(id);

        // 判断博客状态是否正在审核
        if (!Objects.equals(BlogStatusEnum.AUDIT.getValue(), blog.getStatus())) {
            log.info("【撤销博客审核】博客状态不可撤销。status={}", BlogStatusEnum.getName(blog.getStatus()));
            throw new CzzException(ResultEnum.REFRESH.getCode(), "博客已不是审核状态，撤销失败！");
        }

        // 判断博客作者是否与当前操作用户一致
        if (!Objects.equals(userPo.getUserId(), blog.getUserId())) {
            log.error("【撤销博客审核】博客作者不是操作用户");
            throw new CzzException(ResultEnum.REQUEST_FAIL.getCode(),
                    "您撤销审核的博客不属于您，撤销操作失败，如有问题，请联系管理员处理！");
        }

        /*
            获取最新一条博客状态记录
         */
        BlogStatusEntity blogStatus = blogStatusService.findLast(id);
        log.info("【撤销博客审核】最新一条博客状态记录：{}", blogStatus);

        /*
            获取博客审核记录
         */
        List<BlogAuditEntity> blogAuditList = blogAuditService.findList(
                Collections.singletonList(id), Collections.singletonList(AuditTypeEnum.NEW_BLOG.getValue())
        );
        if (blogAuditList.isEmpty()) {
            log.error("【撤销博客审核】博客审核记录不存在");
        }

        // 删除缓存
        String redisKey = CacheKeyUtils.getRedisHashKey(EntityConstant.BLOG);
        String hashKey = id.toString();
        Long count = redisHashHandler.delete(redisKey, hashKey);
        log.info("【撤销博客审核】删除缓存成功。count={}", count);

        /*
            修改博客状态
         */
        BlogEntity updateBlog = new BlogEntity();
        updateBlog.setId(blog.getId())
                .setStatus(blogStatus.getPreStatus())
                .setVersion(blog.getVersion() - 1);
        blogService.edit(updateBlog);

        /*
            删除博客正文
         */
        blogContentService.delete(id, blog.getVersion());

        /*
            删除博客标签
         */
        blogTagService.delete(id, blog.getVersion());

        /*
            保存博客状态记录
         */
        BlogStatusEntity saveBlogStatus = new BlogStatusEntity();
        saveBlogStatus.setBlogId(blog.getId())
                .setAuditId(CommonEnum.ZERO.getLongValue())
                .setPreStatus(blog.getStatus())
                .setCurStatus(updateBlog.getStatus())
                .setRemark("撤销审核");
        blogStatusService.saveOrEdit(saveBlogStatus);

        /*
            删除博客审核记录
         */
        List<BlogAuditEntity> updateBlogAuditList = blogAuditList.stream()
                .filter(blogAudit -> Objects.equals(blogAudit.getAuditStatus(), AuditStatusEnum.WAIT.getValue()))
                .map(blogAudit -> {
                    BlogAuditEntity updateBlogAudit = new BlogAuditEntity();
                    updateBlogAudit.setId(blogAudit.getId())
                            .setAuditStatus(AuditStatusEnum.WITHDRAW.getValue())
                            .setUpdateTime(date)
                            .setUpdateUser(userPo.getUsername())
                            .setUpdateUserId(userPo.getUserId())
                            .setRemark("用户主动撤销审核");
                    return updateBlogAudit;
                })
                .collect(Collectors.toList());
        if (!updateBlogAuditList.isEmpty()) {
            blogAuditService.batchUpdate(updateBlogAuditList);
        }

        // 删除缓存
        this.deleteBlogCache(userPo.getUserId());

        // 延时双删
        Runnable runnable = () -> {
            // 删除缓存
            Long _count = redisHashHandler.delete(redisKey, hashKey);
            log.info("【撤销博客审核】延时双删缓存成功。count={}", _count);
        };
        debounceHandler.debounceRun(runnable, redisKey + Constant.SPLIT + hashKey, 1500, TimeUnit.MILLISECONDS);
    }

    public void withdrawEditAudit(Long id) {
        // 获取操作用户
        UserPo userPo = SecurityUtils.getUser();
        Date date = new Date();

        /*
            获取博客
         */
        BlogEntity blog = blogHelper.getBlog(id);

        // 判断博客状态是否正在审核
        if (!Objects.equals(BlogStatusEnum.EDIT_AUDIT.getValue(), blog.getStatus())) {
            log.info("【撤销博客编辑审核】博客状态不可撤销。status={}", BlogStatusEnum.getName(blog.getStatus()));
            throw new CzzException(ResultEnum.REFRESH.getCode(), "博客已不是审核状态，撤销失败！");
        }

        // 判断博客作者是否与当前操作用户一致
        if (!Objects.equals(userPo.getUserId(), blog.getUserId())) {
            log.error("【撤销博客编辑审核】博客作者不是操作用户");
            throw new CzzException(ResultEnum.REQUEST_FAIL.getCode(),
                    "您撤销审核的博客不属于您，撤销操作失败，如有问题，请联系管理员处理！");
        }

        /*
            获取最新一条博客状态记录
         */
        BlogStatusEntity blogStatus = blogStatusService.findLast(id);
        log.info("【撤销博客编辑审核】最新一条博客状态记录：{}", blogStatus);

        /*
            获取博客审核记录
         */
        List<BlogAuditEntity> blogAuditList = blogAuditService.findList(
                Collections.singletonList(id), Collections.singletonList(AuditTypeEnum.UPDATE_BLOG.getValue())
        );
        if (blogAuditList.isEmpty()) {
            log.error("【撤销博客编辑审核】博客审核记录不存在");
        }

        // 删除缓存
        String redisKey = CacheKeyUtils.getRedisHashKey(EntityConstant.BLOG);
        String hashKey = id.toString();
        Long count = redisHashHandler.delete(redisKey, hashKey);
        log.info("【撤销博客编辑审核】删除缓存成功。count={}", count);

        /*
            修改博客状态
         */
        BlogEntity editBlog = new BlogEntity();
        editBlog.setId(blog.getId())
                .setStatus(blogStatus.getPreStatus())
                .setVersion(blog.getVersion() - 1);
        blogService.edit(editBlog);

        /*
            删除博客正文
         */
        blogContentService.delete(id, blog.getVersion());

        /*
            删除博客标签
         */
        blogTagService.delete(id, blog.getVersion());

        /*
            保存博客状态记录
         */
        BlogStatusEntity saveBlogStatus = new BlogStatusEntity();
        saveBlogStatus.setBlogId(blog.getId())
                .setAuditId(CommonEnum.ZERO.getLongValue())
                .setPreStatus(blog.getStatus())
                .setCurStatus(editBlog.getStatus())
                .setRemark("撤销审核");
        blogStatusService.saveOrEdit(saveBlogStatus);

        /*
            删除博客审核记录
         */
        List<BlogAuditEntity> updateBlogAuditList = blogAuditList.stream()
                .filter(blogAudit -> Objects.equals(blogAudit.getAuditStatus(), AuditStatusEnum.WAIT.getValue()))
                .map(blogAudit -> {
                    BlogAuditEntity updateBlogAudit = new BlogAuditEntity();
                    updateBlogAudit.setId(blogAudit.getId())
                            .setAuditStatus(AuditStatusEnum.WITHDRAW.getValue())
                            .setUpdateTime(date)
                            .setUpdateUser(userPo.getUsername())
                            .setUpdateUserId(userPo.getUserId())
                            .setRemark("用户主动撤销审核");
                    return updateBlogAudit;
                })
                .collect(Collectors.toList());
        if (!updateBlogAuditList.isEmpty()) {
            blogAuditService.batchUpdate(updateBlogAuditList);
        }

        // 删除缓存
        this.deleteBlogCache(userPo.getUserId());

        // 延时双删
        Runnable runnable = () -> {
            // 删除缓存
            Long _count = redisHashHandler.delete(redisKey, hashKey);
            log.info("【撤销博客编辑审核】延时双删缓存成功。count={}", _count);
        };
        debounceHandler.debounceRun(runnable, redisKey + Constant.SPLIT + hashKey, 1500, TimeUnit.MILLISECONDS);
    }

    public void withdrawAppealAudit(Long id) {
        // 获取操作用户
        UserPo userPo = SecurityUtils.getUser();
        Date date = new Date();

        /*
            获取博客
         */
        BlogEntity blog = blogHelper.getBlog(id);

        // 判断博客状态是否正在审核
        if (!Objects.equals(BlogStatusEnum.APPEAL_AUDIT.getValue(), blog.getStatus())) {
            log.info("【撤销博客申诉审核】博客状态不可撤销。status={}", BlogStatusEnum.getName(blog.getStatus()));
            throw new CzzException(ResultEnum.REFRESH.getCode(), "博客已不是审核状态，撤销失败！");
        }

        // 判断博客作者是否与当前操作用户一致
        if (!Objects.equals(userPo.getUserId(), blog.getUserId())) {
            log.error("【撤销博客申诉审核】博客作者不是操作用户");
            throw new CzzException(ResultEnum.REQUEST_FAIL.getCode(),
                    "您撤销审核的博客不属于您，撤销操作失败，如有问题，请联系管理员处理！");
        }

        /*
            获取最新一条博客状态记录
         */
        BlogStatusEntity blogStatus = blogStatusService.findLast(id);
        log.info("【撤销博客申诉审核】最新一条博客状态记录：{}", blogStatus);

        /*
            获取博客审核记录
         */
        List<BlogAuditEntity> blogAuditList = blogAuditService.findList(
                Collections.singletonList(id), Collections.singletonList(AuditTypeEnum.APPEAL_BLOG.getValue())
        );
        if (blogAuditList.isEmpty()) {
            log.error("【撤销博客申诉审核】博客审核记录不存在");
        }

        // 删除缓存
        String redisKey = CacheKeyUtils.getRedisHashKey(EntityConstant.BLOG);
        String hashKey = id.toString();
        Long count = redisHashHandler.delete(redisKey, hashKey);
        log.info("【撤销博客申诉审核】删除缓存成功。count={}", count);

        /*
            修改博客状态
         */
        BlogEntity editBlog = new BlogEntity();
        editBlog.setId(blog.getId())
                .setStatus(blogStatus.getPreStatus())
                .setVersion(blog.getVersion() - 1);
        blogService.edit(editBlog);

        /*
            删除博客正文
         */
        blogContentService.delete(id, blog.getVersion());

        /*
            删除博客标签
         */
        blogTagService.delete(id, blog.getVersion());

        /*
            保存博客状态记录
         */
        BlogStatusEntity saveBlogStatus = new BlogStatusEntity();
        saveBlogStatus.setBlogId(blog.getId())
                .setAuditId(CommonEnum.ZERO.getLongValue())
                .setPreStatus(blog.getStatus())
                .setCurStatus(editBlog.getStatus())
                .setRemark("撤销审核");
        blogStatusService.saveOrEdit(saveBlogStatus);

        /*
            删除博客审核记录
         */
        List<BlogAuditEntity> updateBlogAuditList = blogAuditList.stream()
                .filter(blogAudit -> Objects.equals(blogAudit.getAuditStatus(), AuditStatusEnum.WAIT.getValue()))
                .map(blogAudit -> {
                    BlogAuditEntity updateBlogAudit = new BlogAuditEntity();
                    updateBlogAudit.setId(blogAudit.getId())
                            .setAuditStatus(AuditStatusEnum.WITHDRAW.getValue())
                            .setUpdateTime(date)
                            .setUpdateUser(userPo.getUsername())
                            .setUpdateUserId(userPo.getUserId())
                            .setRemark("用户主动撤销审核");
                    return updateBlogAudit;
                })
                .collect(Collectors.toList());
        if (!updateBlogAuditList.isEmpty()) {
            blogAuditService.batchUpdate(updateBlogAuditList);
        }

        // 删除缓存
        this.deleteBlogCache(userPo.getUserId());

        // 延时双删
        Runnable runnable = () -> {
            // 删除缓存
            Long _count = redisHashHandler.delete(redisKey, hashKey);
            log.info("【撤销博客申诉审核】延时双删缓存成功。count={}", _count);
        };
        debounceHandler.debounceRun(runnable, redisKey + Constant.SPLIT + hashKey, 1500, TimeUnit.MILLISECONDS);
    }

    @Transactional
    public void updateBlog(BlogVo blogVo) {
        // 获取操作用户
        UserPo userPo = SecurityUtils.getUser();
        Date date = new Date();

        /*
            获取博客
         */
        BlogEntity blog = blogHelper.getBlog(blogVo.getId());

        // 判断博客状态是否可编辑
        List<Integer> canEditList = BlogStatusEnum.getCanEditList();
        if (!canEditList.contains(blog.getStatus())) {
            log.info("【修改博客】博客状态不可编辑。status={}", BlogStatusEnum.getName(blog.getStatus()));
            throw new CzzException(ResultEnum.ILLEGAL_FAIL);
        }

        // 判断博客作者是否与当前操作用户一致
        if (!Objects.equals(userPo.getUserId(), blog.getUserId())) {
            log.error("【修改博客】博客作者不是操作用户");
            throw new CzzException(ResultEnum.ILLEGAL_FAIL);
        }

        // 删除缓存
        String redisKey = CacheKeyUtils.getRedisHashKey(EntityConstant.BLOG);
        String hashKey = blog.getId().toString();
        Long count = redisHashHandler.delete(redisKey, hashKey);
        log.info("【修改博客】删除缓存成功。count={}", count);

        /*
            修改博客
         */
        BlogEntity editBlog = new BlogEntity();
        editBlog.setId(blog.getId())
                .setSpaceId(blogVo.getSpaceId())
                .setTitle(blogVo.getTitle())
                .setGuide(blogVo.getGuide())
                .setFirstPicture(blogVo.getFirstPicture())
                .setLabel(blogVo.getLabel())
                .setShowStatement(blogVo.getShowStatement())
                .setOpenComment(blogVo.getOpenComment())
                .setStatus(BlogStatusEnum.EDIT_AUDIT.getValue())
                .setVersion(blog.getVersion() + 1);
        blogService.edit(editBlog);

        /*
            新增博客正文
         */
        BlogContentEntity saveBlogContent = new BlogContentEntity();
        saveBlogContent.setBlogId(blog.getId())
                .setCode(blog.getCode())
                .setVersion(editBlog.getVersion())
                .setContent(blogVo.getContent());
        blogContentService.saveOrEdit(saveBlogContent);

        List<BlogTagEntity> saveBlogTagList = new ArrayList<>();

        List<String> tagList = blogVo.getTagList();
        int sort = 1;
        for (String tagName : tagList) {
            BlogTagEntity saveBlogTag = new BlogTagEntity();
            saveBlogTag.setBlogId(editBlog.getId())
                    .setTagName(tagName)
                    .setSort(sort++)
                    .setVersion(editBlog.getVersion())
                    .setDeleteStatus(CommonEnum.ZERO.getValue())
                    .setUpdateTime(date)
                    .setUpdateUser(StringUtils.EMPTY)
                    .setUpdateUserId(CommonEnum.ZERO.getLongValue())
                    .setCreateTime(date)
                    .setCreateUser(userPo.getUsername())
                    .setCreateUserId(userPo.getUserId());
            saveBlogTagList.add(saveBlogTag);
        }

        /*
            保存博客标签
         */
        if (!saveBlogTagList.isEmpty()) {
            blogTagService.batchSave(saveBlogTagList);
        }

        /*
            保存博客状态记录
         */
        BlogStatusEntity saveBlogStatus = new BlogStatusEntity();
        saveBlogStatus.setBlogId(blog.getId())
                .setAuditId(CommonEnum.ZERO.getLongValue())
                .setPreStatus(blog.getStatus())
                .setCurStatus(editBlog.getStatus())
                .setRemark(StringUtils.EMPTY);
        blogStatusService.saveOrEdit(saveBlogStatus);

        /*
            保存博客审核记录
         */
        BlogAuditEntity saveBlogAudit = new BlogAuditEntity();
        saveBlogAudit.setBlogId(blog.getId())
                .setType(AuditTypeEnum.UPDATE_BLOG.getValue())
                .setLevel(AuditLevelEnum.ONE.getValue())
                .setAuditUserId(CommonEnum.ZERO.getLongValue())
                .setAuditTime(date)
                .setAuditStatus(AuditStatusEnum.WAIT.getValue())
                .setAuditRejectType(CommonEnum.MINUS_ONE.getValue())
                .setRemark(StringUtils.EMPTY);
        blogAuditService.saveOrEdit(saveBlogAudit);

        // 发送审核MQ消息
        BlogAuditMessage blogAuditMessage = new BlogAuditMessage();
        blogAuditMessage.setAuditId(saveBlogAudit.getId())
                .setBlogId(blog.getId())
                .setAuditType(AuditTypeEnum.UPDATE_BLOG.getValue())
                .setAuditLevel(AuditLevelEnum.ONE.getValue());
        rabbitHelper.send(MqEnum.BLOG_AUDIT, blogAuditMessage);

        asyncService.execute(() -> {
            /*
                保存博客流水
             */
            BlogFlowEntity blogFlow = new BlogFlowEntity();
            blogFlow.setBlogId(blog.getId())
                    .setTitle(blog.getTitle())
                    .setGuide(blog.getGuide())
                    .setFirstPicture(blog.getFirstPicture())
                    .setLabel(blog.getLabel())
                    .setShowStatement(blog.getShowStatement())
                    .setOpenComment(blog.getOpenComment())
                    .setVersion(blog.getVersion());
            blogFlowService.saveOrEdit(blogFlow);
        });

        // 延时双删
        Runnable runnable = () -> {
            // 删除缓存
            Long _count = redisHashHandler.delete(redisKey, hashKey);
            log.info("【修改博客】延时双删缓存成功。count={}", _count);
        };
        debounceHandler.debounceRun(runnable, redisKey + Constant.SPLIT + hashKey, 1500, TimeUnit.MILLISECONDS);

        // 删除缓存
        this.deleteBlogCache(userPo.getUserId());

    }

    public void updateBlogFieldStatus(Long id, Integer status, Boolean bool) {
        // 获取请求用户
        UserPo userPo = SecurityUtils.getUser();
        Date date = new Date();

        // 删除博客缓存
        String redisKey = CacheKeyUtils.getRedisHashKey(EntityConstant.BLOG);
        String hashKey = id.toString();
        Long count = redisHashHandler.delete(redisKey, hashKey);
        log.info("【修改博客版权或者评论状态】删除缓存成功。count={}", count);

        /*
            修改博客
         */
        BlogEntity editBlog = new BlogEntity();
        // 设置博客id、更新时间和操作更新的用户id属性值
        editBlog.setId(id);
        if (bool) {
            // 设置最新博客评论状态
            editBlog.setOpenComment(status);
        } else {
            // 设置最新博客版权状态
            editBlog.setShowStatement(status);
        }
        blogService.edit(editBlog);

        // 延时双删
        Runnable runnable = () -> {
            // 删除缓存
            Long _count = redisHashHandler.delete(redisKey, hashKey);
            log.info("【修改博客版权或者评论状态】延时双删缓存成功。count={}", _count);
        };
        debounceHandler.debounceRun(runnable, redisKey + Constant.SPLIT + hashKey, 1500, TimeUnit.MILLISECONDS);

        // 删除缓存
        this.deleteBlogCache(userPo.getUserId());
    }

    public void issueBlog(Long id) {
        // 获取请求用户
        UserPo userPo = SecurityUtils.getUser();
        Date date = new Date();

        /*
            获取博客
         */
        BlogEntity blog = blogHelper.getBlog(id);
        // 判断博客状态是否可发布
        List<Integer> canIssueStatusList = Arrays.asList(
                BlogStatusEnum.AUDIT_PASS.getValue(),
                BlogStatusEnum.HIDE.getValue()
        );
        if (!canIssueStatusList.contains(blog.getStatus())) {
            log.info("【发布博客】博客状态不可发布。status={}", BlogStatusEnum.getName(blog.getStatus()));
            throw new CzzException(ResultEnum.REFRESH.getCode(), "您要发布的博客状态已变更，请确认后重新操作");
        }
        // 判断博客作者是否与当前操作用户一致
        if (!Objects.equals(userPo.getUserId(), blog.getUserId())) {
            log.error("【发布博客】博客作者不是操作用户");
            throw new CzzException(ResultEnum.REQUEST_FAIL.getCode(),
                    "您发布的博客不属于您，发布操作失败，如有问题，请联系管理员处理！");
        }

        // 删除博客缓存
        String redisKey = CacheKeyUtils.getRedisHashKey(EntityConstant.BLOG);
        String hashKey = id.toString();
        Long count = redisHashHandler.delete(redisKey, hashKey);
        log.info("【发布博客】删除缓存成功。count={}", count);

        transactionService.execute(() -> {

            /*
                修改博客
             */
            BlogEntity editBlog = new BlogEntity();
            editBlog.setId(blog.getId())
                   .setStatus(BlogStatusEnum.ISSUE.getValue());
            blogService.edit(editBlog);

            /*
                新增已发布博客
             */
            BlogIssueEntity saveBlogIssue = new BlogIssueEntity();
            saveBlogIssue.setBlogId(blog.getId())
                    .setUserId(userPo.getUserId())
                    .setHotStatus(CommonEnum.ONE.getValue())
                    .setEliteStatus(CommonEnum.ONE.getValue())
                    .setIssueTime(date);
            blogIssueService.insert(saveBlogIssue);

            /*
                保存博客状态记录
             */
            BlogStatusEntity saveBlogStatus = new BlogStatusEntity();
            saveBlogStatus.setBlogId(blog.getId())
                    .setAuditId(CommonEnum.ZERO.getLongValue())
                    .setPreStatus(blog.getStatus())
                    .setCurStatus(editBlog.getStatus());
            blogStatusService.saveOrEdit(saveBlogStatus);

        });

        /*
            发送消息
         */
        // 发送一条消息到MQ中，异步更新系统博客缓存信息
        BlogMessage blogMessage = new BlogMessage(id, BlogMessageEnum.ADD.getValue());
        rabbitHelper.send(MqEnum.BLOG_FANOUT, blogMessage);

        // 延时双删
        Runnable runnable = () -> {
            // 删除缓存
            Long _count = redisHashHandler.delete(redisKey, hashKey);
            log.info("【发布博客】延时双删缓存成功。count={}", _count);
        };
        debounceHandler.debounceRun(runnable, redisKey + Constant.SPLIT + hashKey, 1500, TimeUnit.MILLISECONDS);

        // 删除缓存
        this.deleteBlogCache(userPo.getUserId());
    }

    public void hideBlog(Long id) {
        // 获取请求用户
        UserPo userPo = SecurityUtils.getUser();

        /*
            获取博客
         */
        BlogEntity blog = blogHelper.getBlog(id);
        // 判断博客状态是否可发布
        if (!Objects.equals(BlogStatusEnum.ISSUE.getValue(), blog.getStatus())) {
            log.info("【隐藏博客】博客状态不可隐藏。status={}", BlogStatusEnum.getName(blog.getStatus()));
            throw new CzzException(ResultEnum.REFRESH.getCode(), "您要隐藏的博客不是已发布状态，请确认后重新操作");
        }
        // 判断博客作者是否与当前操作用户一致
        if (!Objects.equals(userPo.getUserId(), blog.getUserId())) {
            log.error("【隐藏博客】博客作者不是操作用户");
            throw new CzzException(ResultEnum.REQUEST_FAIL.getCode(),
                    "您隐藏的博客不属于您，隐藏操作失败，如有问题，请联系管理员处理！");
        }

        // 删除博客缓存
        String redisKey = CacheKeyUtils.getRedisHashKey(EntityConstant.BLOG);
        String hashKey = id.toString();
        Long count = redisHashHandler.delete(redisKey, hashKey);
        log.info("【隐藏博客】删除缓存成功。count={}", count);

        transactionService.execute(() -> {

            /*
                修改博客
             */
            BlogEntity editBlog = new BlogEntity();
            editBlog.setId(blog.getId())
                    .setStatus(BlogStatusEnum.HIDE.getValue());
            blogService.edit(editBlog);

            /*
                修改已发布博客
             */
            BlogIssueEntity saveBlogIssue = new BlogIssueEntity();
            saveBlogIssue.setBlogId(blog.getId())
                    .setDeleteStatus(CommonEnum.ONE.getValue());
            blogIssueService.edit(saveBlogIssue);

            /*
                保存博客状态记录
             */
            BlogStatusEntity saveBlogStatus = new BlogStatusEntity();
            saveBlogStatus.setBlogId(blog.getId())
                    .setAuditId(CommonEnum.ZERO.getLongValue())
                    .setPreStatus(blog.getStatus())
                    .setCurStatus(editBlog.getStatus());
            blogStatusService.saveOrEdit(saveBlogStatus);

        });

        /*
            发送消息
         */
        // 发送一条消息到MQ中，异步更新系统博客缓存信息
        BlogMessage blogMessage = new BlogMessage(id, BlogMessageEnum.REMOVE.getValue());
        rabbitHelper.send(MqEnum.BLOG_FANOUT, blogMessage);

        // 延时双删
        Runnable runnable = () -> {
            // 删除缓存
            Long _count = redisHashHandler.delete(redisKey, hashKey);
            log.info("【隐藏博客】延时双删缓存成功。count={}", _count);
        };
        debounceHandler.debounceRun(runnable, redisKey + Constant.SPLIT + hashKey, 1500, TimeUnit.MILLISECONDS);

        // 删除缓存
        this.deleteBlogCache(userPo.getUserId());
    }

    public void recommendBlog(Long id) {
        // 获取操作用户
        UserPo userPo = SecurityUtils.getUser();

        String lockKey = RedisConstant.LOCK + EntityConstant.BLOG + id;
        RLock lock = redissonClient.getLock(lockKey);
        // 尝试加锁
        boolean bool = lock.tryLock();
        log.info("推荐博客尝试加锁完成。lockKey={} bool={}", lockKey, bool);
        if (!bool) {
            throw new CzzException(ResultEnum.REFRESH.getCode(),
                    "您推荐的博客正在被操作中，此次操作无效，如有问题，请联系管理员处理。");
        }

        try {
            /*
                获取博客
             */
            BlogEntity blog = blogHelper.getBlog(id);

            /*
                获取已发布的博客信息
             */
            BlogIssueEntity issueBlog = blogIssueService.getBlogIssue(id);

            if (issueBlog.getEliteStatus().equals(CommonEnum.ONE.getValue())) {
                /*
                    博客的推荐状态为’不推荐‘，将推荐状态改为‘推荐’
                 */

                transactionService.execute(() -> {
                    /*
                        更新已发布博客
                     */
                    BlogIssueEntity editBlogIssue = new BlogIssueEntity();
                    editBlogIssue.setBlogId(id)
                            .setEliteStatus(CommonEnum.ZERO.getValue());
                    blogIssueService.edit(editBlogIssue);

                    /*
                        添加博客博客状态记录
                     */
                    BlogStatusEntity saveBlogStatus = new BlogStatusEntity();
                    saveBlogStatus.setBlogId(id)
                            .setAuditId(CommonEnum.ZERO.getLongValue())
                            .setPreStatus(blog.getStatus())
                            .setCurStatus(BlogStatusEnum.RECOMMEND.getValue())
                            .setRemark(StringUtils.EMPTY);
                    blogStatusService.saveOrEdit(saveBlogStatus);

                    String message = "您的‘" + blog.getTitle() + "’博客被平台推荐了，请查看。";

                    /*
                        给用户发送消息
                     */
                    userMessageService.saveUserMessage(issueBlog.getUserId(), userPo.getUserId(),
                            UserMessageEnum.SYSTEM_MSG.getValue(), "博客推荐通知", message);
                });

                /*
                    删除缓存
                 */
                // 删除管理员管理的博客列表临时缓存
                this.deleteBlogCache(issueBlog.getUserId());
            } else {
                /*
                    博客的推荐状态为’推荐‘
                 */
                /*
                    获取博客状态记录，判断博客被谁提前推荐了
                 */
                BlogStatusEntity blogStatus = blogStatusService.findLastStatus(id, null, BlogStatusEnum.RECOMMEND.getValue());
                Long createUserId = blogStatus.getCreateUserId();

                String message = "博客已经被推荐过了，无需重复操作!";
                if (!Objects.equals(createUserId, CommonEnum.ZERO.getLongValue())) {

                    /*
                        获取用户名称
                     */
                    String name = userHelper.getUsername(createUserId);
                    if (StringUtils.isNotEmpty(name)) {
                        message = "博客已经被’" + name + "‘推荐了，无需重复操作!";
                    }
                }
                throw new CzzException(ResultEnum.REFRESH.getCode(), message);
            }
        } finally {
            // 判断当前锁对象是否还持有锁 并 持有该锁的线程与当前业务线程一致
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                // 解锁
                lock.unlock();
            }
        }
    }

    @Transactional
    public void cancelRecommendBlog(Long id) {

        String lockKey = RedisConstant.LOCK + EntityConstant.BLOG + id;
        RLock lock = redissonClient.getLock(lockKey);
        // 尝试加锁
        boolean bool = lock.tryLock();
        log.info("取消博客推荐尝试加锁完成。lockKey={} bool={}", lockKey, bool);
        if (!bool) {
            throw new CzzException(ResultEnum.REFRESH.getCode(),
                    "您取消推荐的博客正在被操作中，此次操作无效，如有问题，请联系管理员处理。");
        }
        try {

            /*
                获取博客
             */
            BlogEntity blog = blogHelper.getBlog(id);

            /*
                获取已发布的博客信息
             */
            BlogIssueEntity issueBlog = blogIssueService.getBlogIssue(id);

            if (issueBlog.getEliteStatus().equals(CommonEnum.ZERO.getValue())) {
                /*
                    博客的推荐状态为’推荐‘，将推荐状态改为‘不推荐’
                 */

                transactionService.execute(() -> {
                    /*
                        更新博客已发布数据
                     */
                    BlogIssueEntity editBlogIssue = new BlogIssueEntity();
                    editBlogIssue.setBlogId(id)
                            .setEliteStatus(CommonEnum.ONE.getValue());
                    blogIssueService.edit(editBlogIssue);

                    /*
                        添加博客博客状态记录
                     */
                    BlogStatusEntity saveBlogStatus = new BlogStatusEntity();
                    saveBlogStatus.setBlogId(id)
                            .setAuditId(CommonEnum.ZERO.getLongValue())
                            .setPreStatus(BlogStatusEnum.RECOMMEND.getValue())
                            .setCurStatus(blog.getStatus())
                            .setRemark(StringUtils.EMPTY);
                    blogStatusService.saveOrEdit(saveBlogStatus);
                });

                /*
                    删除缓存
                 */
                this.deleteBlogCache(issueBlog.getUserId());
            } else {
                /*
                    博客的推荐状态为’不推荐‘
                 */

                /*
                    获取博客状态记录，判断博客被谁提前取消推荐了
                 */
                BlogStatusEntity blogStatus = blogStatusService.findLastStatus(id, BlogStatusEnum.RECOMMEND.getValue(), null);
                Long createUserId = blogStatus.getCreateUserId();

                String message = "博客已经取消推荐了，无需重复操作!";
                if (Objects.equals(createUserId, CommonEnum.ZERO.getLongValue())) {

                    /*
                        获取用户名称
                     */
                    String name = userHelper.getUsername(createUserId);
                    if (StringUtils.isNotEmpty(name)) {
                        message = "博客已经被’" + name + "‘取消推荐了，无需重复操作!";
                    }
                }
                throw new CzzException(ResultEnum.REFRESH.getCode(), message);
            }
        } finally {
            // 判断当前锁对象是否还持有锁 并 持有该锁的线程与当前业务线程一致
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                // 解锁
                lock.unlock();
            }
        }
    }

    public void outBlog(OutBlogVo outBlogVo) {
        // 获取正在执行博客下架的用户信息对象
        UserPo userPo = SecurityUtils.getUser();

        String lockKey = RedisConstant.LOCK + EntityConstant.BLOG + outBlogVo.getId();
        // 获取Redis分布式锁对象
        RLock lock = redissonClient.getLock(lockKey);
        // 尝试加锁
        boolean bool = lock.tryLock();
        log.info("博客下架尝试加锁完成。lockKey={} bool={}", lockKey, bool);
        if (!bool) {
            throw new CzzException(ResultEnum.REFRESH.getCode(),
                    "您下架的博客正在被操作中，此次操作无效，如有问题，请联系管理员处理。");
        }
        try {
            /*
                获取博客
             */
            BlogEntity blog = blogHelper.getBlog(outBlogVo.getId());
            if (!BlogStatusEnum.ISSUE.getValue().equals(blog.getStatus())) {
                throw new CzzException(ResultEnum.REFRESH.getCode(),
                        "您下架的博客不是已发布状态，请确认，如有问题，请联系管理员处理。");
            }

            /*
                获取已发布的博客信息
             */
            BlogIssueEntity issueBlog = blogIssueService.getBlogIssue(outBlogVo.getId());

            // 删除博客缓存
            String redisKey = CacheKeyUtils.getRedisHashKey(EntityConstant.BLOG);
            String hashKey = outBlogVo.getId().toString();
            Long count = redisHashHandler.delete(redisKey, hashKey);
            log.info("【博客下架】删除缓存成功。count={}", count);

            transactionService.execute(() -> {
                /*
                    更新博客
                 */
                BlogEntity editBlog = new BlogEntity();
                editBlog.setId(outBlogVo.getId())
                        .setStatus(BlogStatusEnum.OFF_LINE.getValue());
                blogService.edit(editBlog);

                /*
                    删除已发布博客
                 */
                blogIssueService.deleteIssueBlog(Collections.singletonList(outBlogVo.getId()));

                /*
                    保存博客状态记录
                 */
                BlogStatusEntity saveBlogStatus = new BlogStatusEntity();
                saveBlogStatus.setBlogId(outBlogVo.getId())
                        .setAuditId(CommonEnum.ZERO.getLongValue())
                        .setPreStatus(blog.getStatus())
                        .setCurStatus(editBlog.getStatus())
                        .setRemark(outBlogVo.getMessage());
                blogStatusService.saveOrEdit(saveBlogStatus);

                /*
                    给用户发送消息
                 */
                String message = "您的博客‘" + blog.getTitle() + "’已经下架了，如有问题，您可以进行申诉操作";
                userMessageService.saveUserMessage(issueBlog.getUserId(), userPo.getUserId(),
                        UserMessageEnum.SYSTEM_MSG.getValue(), "博客下架通知", message);
            });

            /*
                发送消息
             */
            // 发送一条消息到MQ中，异步删除系统博客缓存信息
            BlogMessage blogMessage = new BlogMessage(outBlogVo.getId(), BlogMessageEnum.REMOVE.getValue());
            rabbitHelper.send(MqEnum.BLOG_FANOUT, blogMessage);

            // 延时双删
            Runnable runnable = () -> {
                // 删除缓存
                Long _count = redisHashHandler.delete(redisKey, hashKey);
                log.info("【博客下架】延时双删缓存成功。count={}", _count);
            };
            debounceHandler.debounceRun(runnable, redisKey + Constant.SPLIT + hashKey, 1500, TimeUnit.MILLISECONDS);

            // 删除管理员管理的博客列表临时缓存
            this.deleteBlogCache(issueBlog.getUserId());
        } finally {
            // 判断当前锁对象是否还持有锁 并 持有该锁的线程与当前业务线程一致
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                // 解锁
                lock.unlock();
            }
        }
    }

    @Transactional
    public void putBlog(Long id) {
        // 获取正在执行博客重新上架操作的用户信息对象
        UserPo userPo = SecurityUtils.getUser();
        Date date = new Date();

        String lockKey = RedisConstant.LOCK + EntityConstant.BLOG + id;
        // 获取Redis分布式锁对象
        RLock lock = redissonClient.getLock(lockKey);
        // 尝试加锁
        boolean bool = lock.tryLock();
        log.info("博客重新上架尝试加锁完成。lockKey={} bool={}", lockKey, bool);
        if (!bool) {
            throw new CzzException(ResultEnum.REFRESH.getCode(),
                    "您重新上架的博客正在被操作中，此次操作无效，如有问题，请联系管理员处理。");
        }
        try {
            /*
                获取博客
             */
            BlogEntity blog = blogHelper.getBlog(id);
            if (!BlogStatusEnum.OFF_LINE.getValue().equals(blog.getStatus())) {
                throw new CzzException(ResultEnum.REFRESH.getCode(),
                        "您重新上架的博客不是下架状态，请确认，如有问题，请联系管理员处理。");
            }

            /*
                获取已发布的博客信息
             */
            BlogIssueEntity blogIssue = blogIssueService.getBlogIssue(id);

            // 删除博客缓存
            String redisKey = CacheKeyUtils.getRedisHashKey(EntityConstant.BLOG);
            String hashKey = id.toString();
            Long count = redisHashHandler.delete(redisKey, hashKey);
            log.info("【上架博客】删除缓存成功。count={}", count);

            transactionService.execute(() -> {
                /*
                    更新博客
                 */
                BlogEntity editBlog = new BlogEntity();
                editBlog.setId(id)
                       .setStatus(BlogStatusEnum.ISSUE.getValue());
                blogService.edit(editBlog);

                /*
                    更新博客已发布数据
                 */
                BlogIssueEntity editBlogIssue = new BlogIssueEntity();
                editBlogIssue.setBlogId(id)
                        .setIssueTime(date)
                        .setDeleteStatus(CommonEnum.ZERO.getValue());
                blogIssueService.edit(editBlogIssue);

                /*
                    保存博客状态记录
                 */
                BlogStatusEntity saveBlogStatus = new BlogStatusEntity();
                saveBlogStatus.setBlogId(id)
                       .setAuditId(CommonEnum.ZERO.getLongValue())
                       .setPreStatus(blog.getStatus())
                       .setCurStatus(editBlog.getStatus());
                blogStatusService.saveOrEdit(saveBlogStatus);

                /*
                   给用户发送消息
                 */
                String message = "您的'" + blog.getTitle() + "'博客，被管理员重新上架了，请查看。";
                userMessageService.saveUserMessage(blog.getUserId(), userPo.getUserId(),
                        UserMessageEnum.SYSTEM_MSG.getValue(), "博客重新上架通知", message);
            });

            /*
                发送消息
             */
            // 发送一条消息到MQ中，异步增加系统博客缓存信息
            BlogMessage blogMessage = new BlogMessage(id, BlogMessageEnum.ADD.getValue());
            rabbitHelper.send(MqEnum.BLOG_FANOUT, blogMessage);

            /*
                删除缓存
             */
            // 删除管理员管理的博客列表临时缓存
            this.deleteBlogCache(blog.getUserId());
        } finally {
            // 判断当前锁对象是否还持有锁 并 持有该锁的线程与当前业务线程一致
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                // 解锁
                lock.unlock();
            }
        }
    }

    public void deleteBlog(List<Long> idList) {
        // 获取操作用户
        UserPo userPo = SecurityUtils.getUser();

        /*
            获取博客
         */
        List<BlogEntity> blogList = blogHelper.getBlogList(idList);
        if (CollectionUtils.isEmpty(blogList)) {
            log.info("【删除博客】博客不存在");
            throw new CzzException(ResultEnum.REQUEST_FAIL.getCode(), "您要删除的博客不存在");
        }

        // 判断博客所属用户是不是操作用户
        boolean bool = blogList.stream().anyMatch(blog -> !Objects.equals(userPo.getUserId(), blog.getUserId()));
        log.info("判断博客所属用户是不是操作用户完成。bool={}", bool);
        if (bool) {
            log.error("【删除博客】博客不属于操作用户");
            throw new CzzException(ResultEnum.REQUEST_FAIL.getCode(),
                    "您要删除的博客不属于您，删除操作失败，如有问题，请联系管理员处理！");
        }


        // 删除博客缓存
        String redisKey = CacheKeyUtils.getRedisHashKey(EntityConstant.BLOG);
        List<String> hashKeyList = CopyUtils.mapList(idList, Objects::toString);
        Long count = redisHashHandler.delete(redisKey, hashKeyList);
        log.info("【删除博客】删除缓存成功。count={}", count);

        transactionService.execute(() -> {
            /*
                删除博客
             */
            blogService.deleteBlog(idList);

            /*
                删除已发布博客
             */
            blogIssueService.deleteIssueBlog(idList);

            /*
                删除待审核博客记录
             */
            blogAuditService.deleteWaitAudit(idList);

        });

        // 延时双删
        Runnable runnable = () -> {
            // 删除缓存
            Long _count = redisHashHandler.delete(redisKey, hashKeyList);
            log.info("【删除博客】延时双删缓存成功。count={}", _count);
        };
        debounceHandler.debounceRun(runnable, redisKey + Constant.SPLIT + hashKeyList, 1500, TimeUnit.MILLISECONDS);

        // 删除管理员管理的博客列表临时缓存
        this.deleteBlogCache(userPo.getUserId());
    }

    public void deleteBlogCache(List<String> idList) {
        // 操作Redis缓存，批量删除博客缓存信息
        String redisKey = CacheKeyUtils.getRedisHashKey(EntityConstant.BLOG);
        redisHashHandler.delete(redisKey, idList);
    }

    public void clearBlogCache() {
        // 操作Redis缓存，清空博客缓存信息
        String redisKey = CacheKeyUtils.getRedisHashKey(EntityConstant.BLOG);
        redisHandler.delete(redisKey);
    }

    /**
     * 删除管理员管理的博客列表临时缓存
     * @param userId 博客所属的用户id
     */
    private void deleteBlogCache(Long userId) {
        List<String> patternKeys = new ArrayList<>(2);
        // 删除用户自己的博客列表临时缓存
        patternKeys.add(RedisConstant.USER_BLOG_LIST + RedisConstant.SPLIT + userId + RedisConstant.ASTERISK);
        // 删除所有管理员管理博客列表临时缓存
        patternKeys.add(RedisConstant.MANAGE_BLOG_LIST + RedisConstant.ASTERISK);
        redisHandler.fuzzyDelete(patternKeys);
    }
}
